﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Services;
using Noris.Schedule.Support.Data;

namespace Noris.Schedule.UI.Components.Deleted
{
	// smazat odsud:

	#region CLASS GridDataRowCls : trvalá data jednotlivých řádků
	/// <summary>
	/// GridDataRowCls : data jednotlivých řádků.
	/// Obsahuje jak data významová (získaná z datového zdroje),
	/// tak data grafická, která popisují způsob renderování,
	/// tak i data grafů (načtené položky a jejich grafická reprezentace).
	/// Záznamy této třídy zůstávají v paměti po jejich načtení stále, bez ohledu na zavření/otevření nodů v Tree.
	/// Za každý jeden GID existuje jen jeden záznam GridDataRowCls, i když by se měl zobrazovat vícekrát.
	/// </summary>
	internal class GridDataRowCls
	{
		#region KONSTRUKCE
		/// <summary>
		/// Privátní defaultní konstruktor 
		/// </summary>
		private GridDataRowCls()
		{ }
		/// <summary>
		/// GID tohoto záznamu = řádek.
		/// </summary>
		internal GID GId { get { return this.RowItem.GId; } }
		/// <summary>
		/// Data položky, získaná z datového zdroje
		/// </summary>
		internal IDataRow RowItem { get { return _RowItem; } }
		private IDataRow _RowItem;
		/// <summary>
		/// true, pokud tento řádek má být vidět (řádkový filtr AND (needituje se nic, anebo právě tento řádek se edituje)).
		/// </summary>
		internal bool RowIsFilterAsVisible { get; set; }
		/// <summary>
		/// Výška tohoto řádku v pixelech. Pokud ji uživatel změní, musí se to propsat sem.
		/// </summary>
		internal int RowHeight { get; set; }
		/// <summary>
		/// Výška tohoto řádku v pixelech, deklarovaná ve vstupních datech násobkem základní výšky řádku.
		/// Jde o minimální výšku.
		/// Pokud bude třeba, systém výšku řádku zvětší, ale nikdy ji nezmenší pod tento údaj.
		/// </summary>
		internal int RowHeightDeclared { get; set; }
		/// <summary>
		/// Informace o časovém rozsahu, pro který jsou načtena data v poli this.ChartElements.
		/// Při změně časového rozmezí se data načítají z podkladové datové vrstvy znovu.
		/// Hodnota Empty znamená, že data dosud nebyla načtena.
		/// </summary>
		internal TimeRange ChartElementLoadedTimeRange = TimeRange.Empty;
		/// <summary>
		/// Informace o časovém rozsahu, pro který jsou kalkulovány dimenze prvku grafu v poli this.ChartElements.
		/// Při změně časového rozmezí se data o rozměrech grafu přepočítají z fyzických dat znovu.
		/// Hodnota Empty znamená, že data dosud nebyla přepočítána.
		/// </summary>
		internal TimeRange ChartElementCalculatedTimeRange = TimeRange.Empty;
		/// <summary>
		/// Informace o rozměru grafu (v pixelech), pro který jsou vypočteny vizuální dimenze v poli this.ChartElements
		/// Při změně rozměru se data nenačítají, jen se přepočtou jejich vizuální parametry.
		/// </summary>
		internal Size ChartElementCalculatedSize = Size.Empty;
		/// <summary>
		/// Dictionary prvků grafu v tomto řádku.
		/// Pokud je null, není načteno.
		/// Tento objekt slouží jako zámek při multithread načítání.
		/// Načítá se při prvním použití a pak při použití po změně datového rozsahu this.ChartElementLoadedTimeRange.
		/// Při změně vizuálních vlastností (šířka / výška grafu) se nenačítá, jen se přepočítá.
		/// </summary>
		internal Dictionary<GID, GridDataChartElementCls> ChartElements;
		/// <summary>
		/// Index (non-unique) : Key = WorkGId (jen pro NotEmpty !!!), Value = List, jehož položky = ElementGID = klíč k elementu do ChartElements.
		/// Index slouží k tomu, abych rychle našel všechny elementy pro jednu konkrétní prácí = WorkGId. 
		/// Rozhodně lepší, než například FindAll(search pro WorkGID), která dělá ScanAll.
		/// </summary>
		internal Dictionary<GID, List<GID>> ChartWorkElements;
		/// <summary>
		/// Jednoduché pole souřadnic Y, na nichž začínají jednotlivé Pipes.
		/// Souřadnice Y jsou relativní vzhledem k řádku (Y = 0 je pro Top pixel řádku).
		/// Pole obsahuje nejméně 1 prvek pro Pipe = 0.
		/// Elementy obsahují číslo PipeLine, v které jsou kresleny, toto číslo odpovídá beze změny indexu do tohoto pole.
		/// Elementy nejsou ve standardním stavu kresleny do Pipe = 0, ta je určena pro přesouvání elementů na jiný čas.
		/// </summary>
		internal int[] ChartPipesY;
		/// <summary>
		/// <para>
		/// Příznak, že po načtení dat proběhla kalkulace návazných údajů, závislá na typu grafu.
		/// Po načtení dat elementů je nahozeno false.
		/// Před zobrazením dat se testuje, a pokud je false pak se spustí kalkulace.
		/// Kalkulace nastaví true.
		/// </para>
		/// <para>
		/// Multithread zámky: po dobu přidávání elementů je zamčen objekt this.ChartElements,
		/// stejně tak po dobu kalkulace návazných údajů se tentýž objekt zamyká.
		/// Tím je zaručeno, že kalkulovaná návazná data odpovídají stavu this.ChartElements a stavu proměnné ChartElementsAfterReadCalculated.
		/// </para>
		/// </summary>
		internal bool ChartElementsAfterReadCalculated;
		/// <summary>
		/// Výška jedné Pipe, je ve všech řádcích shodná. Výška celého řádku = ChartPipeHeight * ChartPipesY.Length + 4 (4 = 1+1 (GridLines) + 1+1 (Space).
		/// </summary>
		internal int ChartPipeHeight;
		/// <summary>
		/// Najde a vrátí element pro daný GID, anebo vrátí null pokud neexistuje.
		/// </summary>
		/// <param name="graphGId">GID hledaného elementu</param>
		/// <returns>Data elementu</returns>
		internal GridDataChartElementCls TryGetChartElement(GID graphGId)
		{
			GridDataChartElementCls element;
			if (ChartElements.TryGetValue(graphGId, out element))
				return element;
			return null;
		}
		/// <summary>
		/// Najde a vrátí soupis GID elementů, které patří pod daný WorkGId.
		/// Pokud nenajde, nebo neexistují, nebo je zadán GID.Empty, vrací null.
		/// </summary>
		/// <param name="workGId"></param>
		/// <returns></returns>
		internal List<GID> GetListChartGIdForWorkGId(GID workGId)
		{
			if (workGId.IsEmpty || this.ChartWorkElements == null) return null;
			List<GID> result;
			if (this.ChartWorkElements.TryGetValue(workGId, out result))
				return result;
			return null;
		}
		/// <summary>
		/// Tříděný seznam viditelných prvků v tomto řádku.
		/// Obsahuje pouze viditelné (i jen zčásti viditelné) prvky, v pořadí jejich InitialTimeRange.Begin.
		/// Objekty v tomto seznamu jsou jen reference do Dictionary ChartElements.
		/// </summary>
		internal List<GridDataChartElementCls> ChartVisibleList;
		/// <summary>
		/// Příznak, že elementy načtené do pole this.ElementList představují úplnou sadu elementů, 
		/// a pro tento řádek (this) nebude nikdy zapotřebí donačítat elementy pro širší časové pole.
		/// </summary>
		internal bool AllElementsLoaded
		{
			get { return _AllElementsLoaded; }
			set
			{
				if (_AllElementsLoaded && !value)
					Throw.SysError(MessageInfo.Get("Není možno zrušit příznak «AllElementsLoaded» z hodnoty true na false."));
				_AllElementsLoaded = value;
			}
		}
		private bool _AllElementsLoaded;
		/// <summary>
		/// Konstruktor s plněním z dat třídy DataItemCls.
		/// Vytvoří objekt s rozšiřujícími daty pro zobrazování grafů na základě standardních dat z datového zdroje.
		/// </summary>
		/// <param name="rowItem">Řádek s vlastními daty</param>
		internal static GridDataRowCls CreateRow(IDataRow rowItem)
		{
			GridDataRowCls row = new GridDataRowCls();
			row._RowItem = rowItem;
			row.ChartElementsAdd(rowItem.ElementList, rowItem.AllElementsLoaded);
			row.RowHeight = 2 + (int)(24 * rowItem.RowHeightMultiplier);
			row.RowHeightDeclared = row.RowHeight;
			return row;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return this.RowItem.ToString();
		}
		#endregion
		#region SPRÁVA ELEMENTŮ - ZÁKLADNÍ (ADD, TEMP, CLEAR)
		/// <summary>
		/// Zajistí přidání dat elementů (časové úseky) z předaného seznamu do svého seznamu elementů.
		/// Na vstupu je seznam elementů třídy DataElementCls = holá datová třída,
		/// do interního seznamu (this.ChartElements) se přidávají elementy zabalené 
		/// do grafické třídy GridDataChartElementCls (obsahuje navíc vizuální údaje).
		/// Současně se nastavuje příznak AllElementsLoaded (=všechny existující elementy jsou načteny, není zapotřebí žádné donačítání).
		/// Metoda vrací příznak, že do řádku byly skutečně přidány některé nově načtené elementy (true) / false = nic nového nebylo přidáno.
		/// </summary>
		/// <param name="elementList">Seznam časových elementů, datová třída</param>
		internal bool ChartElementsAdd(List<IDataElement> elementList, bool allElementLoaded)
		{
			bool result = ChartElementsAdd(elementList);
			if (elementList != null && allElementLoaded && !this.AllElementsLoaded)
				this.AllElementsLoaded = allElementLoaded;
			return result;
		}
		/// <summary>
		/// Zajistí přidání dat elementů (časové úseky) z předaného seznamu do svého seznamu elementů.
		/// Na vstupu je seznam elementů třídy DataElementCls = holá datová třída,
		/// do interního seznamu (this.ChartElements) se přidávají elementy zabalené 
		/// do grafické třídy GridDataChartElementCls (obsahuje navíc vizuální údaje).
		/// Současně se nastavuje příznak AllElementsLoaded (=všechny existující elementy jsou načteny, není zapotřebí žádné donačítání).
		/// Metoda vrací příznak, že do řádku byly skutečně přidány některé nově načtené elementy (true) / false = nic nového nebylo přidáno.
		/// </summary>
		/// <param name="element">Jeden element, který přidávám do soupisu elementů v řádku</param>
		/// <returns>Příznak, že do řádku byly skutečně přidány některé nově načtené elementy (true) / false = nic nového nebylo přidáno.</returns>
		internal bool ChartElementsAdd(IDataElement element)
		{
			return this.ChartElementsAdd(new IDataElement[] { element });
		}
		/// <summary>
		/// Zajistí přidání dat elementů (časové úseky) z předaného seznamu do svého seznamu elementů.
		/// Na vstupu je seznam elementů třídy DataElementCls = holá datová třída,
		/// do interního seznamu (this.ChartElements) se přidávají elementy zabalené 
		/// do grafické třídy GridDataChartElementCls (obsahuje navíc vizuální údaje).
		/// Současně se nastavuje příznak AllElementsLoaded (=všechny existující elementy jsou načteny, není zapotřebí žádné donačítání).
		/// Metoda vrací příznak, že do řádku byly skutečně přidány některé nově načtené elementy (true) / false = nic nového nebylo přidáno.
		/// </summary>
		/// <param name="elementList">Seznam časových elementů, datová třída</param>
		/// <returns>Příznak, že do řádku byly skutečně přidány některé nově načtené elementy (true) / false = nic nového nebylo přidáno.</returns>
		internal bool ChartElementsAdd(IList<IDataElement> elementList)
		{
			// Pokud dosud není připraven soupis elementů, připravíme jej prázdný právě teď :
			if (this.ChartElements == null) this.ChartElements = new Dictionary<GID, GridDataChartElementCls>();
			if (this.ChartWorkElements == null) this.ChartWorkElements = new Dictionary<GID, List<GID>>();

			// Pokud není předán seznam elementů, pak nic neděláme = neznámá vstupní data:
			if (elementList == null) return false;

			// Pokud je předaný seznam elementů prázdný, pak nic neukládáme:
			int addCount = 0;
			if (elementList.Count > 0)
			{	// Máme vstupní data, teprve tehdy potřebujeme multithread zámek na soupis elementů:
				lock (this.ChartElements)
				{	// Zámek nám zajistí, že po dobu přidávání záznamů do this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
					foreach (IDataElement dataElement in elementList)
					{
						if (this.ChartElements.ContainsKey(dataElement.GId)) continue; // Ty elementy, které už známe, můžeme přeskočit.

						GridDataChartElementCls gridElement = new GridDataChartElementCls(dataElement);
						this.ChartElements.Add(dataElement.GId, gridElement);

						// Přidat GID element i do indexu WorkGId:
						this._AddGIdToWorks(dataElement);

						addCount++;
					}
					if (addCount > 0)
						// Pokud jsem skutečně přidal nějaký nový element, 
						//  shodím si příznak, že načtená data prošla kalkulací. Kalkulace se vyvolá před zobrazením.
						this.ChartElementsAfterReadCalculated = false;
				}
			}
			return (addCount > 0);
		}
		/// <summary>
		/// Zajistí přidání dat elementů (časové úseky) z předaného seznamu do svého seznamu elementů.
		/// Na vstupu je seznam elementů třídy DataElementCls = holá datová třída,
		/// do interního seznamu (this.ChartElements) se přidávají elementy zabalené 
		/// do grafické třídy GridDataChartElementCls (obsahuje navíc vizuální údaje).
		/// Současně se nastavuje příznak AllElementsLoaded (=všechny existující elementy jsou načteny, není zapotřebí žádné donačítání).
		/// Metoda vrací příznak, že do řádku byly skutečně přidány některé nově načtené elementy (true) / false = nic nového nebylo přidáno.
		/// </summary>
		/// <param name="gridElement">Element grafu, už obalený do objektu třídy GridDataChartElementCls.
		/// Tato varianta se používá při přenášení elementu z jednoho řádku do jiného.</param>
		/// <returns>Příznak, že do řádku byly skutečně přidány některé nově načtené elementy (true) / false = nic nového nebylo přidáno.</returns>
		internal bool ChartElementsAdd(GridDataChartElementCls gridElement)
		{
			if (gridElement == null) return false;
			// Pokud dosud není připraven soupis elementů, připravíme jej prázdný právě teď :
			if (this.ChartElements == null) this.ChartElements = new Dictionary<GID, GridDataChartElementCls>();
			if (this.ChartWorkElements == null) this.ChartWorkElements = new Dictionary<GID, List<GID>>();

			bool isAdded = false;
			lock (this.ChartElements)
			{	// Zámek nám zajistí, že po dobu přidávání záznamů do this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
				if (!this.ChartElements.ContainsKey(gridElement.GId))
				{
					this.ChartElements.Add(gridElement.GId, gridElement);

					// Přidat GID element i do indexu WorkGId:
					this._AddGIdToWorks(gridElement.DataElement);

					// Pokud jsem skutečně přidal nějaký nový element, 
					//  shodím si příznak, že načtená data prošla kalkulací. Kalkulace se vyvolá před zobrazením.
					this.ChartElementsAfterReadCalculated = false;

					isAdded = true;
				}
			}

			return (isAdded);
		}
		/// <summary>
		/// Přidá data z daného elementu (GID, WorkGId) do interního soupisu WorkGId =» ElementGId
		/// </summary>
		/// <param name="dataElement"></param>
		private void _AddGIdToWorks(IDataElement dataElement)
		{
			if (dataElement.WorkGId.NotEmpty)
			{	// Jen pro neprázdné WorkGId:
				List<GID> workGIdList;
				if (!this.ChartWorkElements.TryGetValue(dataElement.WorkGId, out workGIdList))
				{
					workGIdList = new List<GID>();
					this.ChartWorkElements.Add(dataElement.WorkGId, workGIdList);
				}
				if (!workGIdList.Contains(dataElement.GId))
					workGIdList.Add(dataElement.GId);
			}
		}
		/// <summary>
		/// Zajistí odebrání daného elementu.
		/// Používá se při editaci, kdy element odeberu z jednoho řádku a přidím jej do jiného.
		/// Současně zajistí invalidaci času, pro který jsou určena vizuální data (pravděpodobně bude třeba přepočítat vizuální data znovu).
		/// </summary>
		/// <param name="elementGId">GID elementu, který chcem vyjmout</param>
		internal bool ChartElementsRemove(GID elementGId)
		{
			bool result = false;
			lock (this.ChartElements)
			{	// Zámek nám zajistí, že po dobu modifikace záznamů v this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
				if (this.ChartElements.ContainsKey(elementGId))
				{
					this.ChartElements.Remove(elementGId);
					result = true;
				}
				// Shodím si příznaky, že načtená data prošla kalkulací. Kalkulace se vyvolá před zobrazením.
				this.ChartElementCalculatedTimeRange = TimeRange.Empty;
				this.ChartElementsAfterReadCalculated = false;
			}
			return result;
		}
		/// <summary>
		/// Zajistí přidání dočasného elementu podle požadavku na načítání do svého seznamu elementů.
		/// Dočasný element vizuálně reprezentuje časový úsek, pro který nyní probíhá načítání dat.
		/// Po dokončení reálného načtení dat bude tento dočasný element odebrán - viz metoda ChartElementsTempDel().
		/// </summary>
		/// <param name="request">Specifikace požadavku</param>
		internal void ChartElementsTempAdd(DataSourceRequestReadElements request)
		{
			GridDataElementCls element = GridDataElementCls.CreateTempElement(request);
			element.TextNote = "Data pro tento čas se načítají.";

			List<IDataElement> list = new List<IDataElement>();
			list.Add(element);
			this.ChartElementsAdd(list);
		}
		/// <summary>
		/// Zajistí odebrání dočasného elementu, vytvořeného podle požadavku na načítání do svého seznamu elementů.
		/// Současně zajistí invalidaci času, pro který jsou určena vizuální data (pravděpodobně bude třeba přepočítat vizuální data znovu).
		/// </summary>
		/// <param name="request"></param>
		internal bool ChartElementsTempDel(DataSourceRequest request)
		{
			bool result = false;
			GID tempGId = request.TempGId;
			lock (this.ChartElements)
			{	// Zámek nám zajistí, že po dobu modifikace záznamů v this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
				if (this.ChartElements.ContainsKey(tempGId))
				{
					this.ChartElements.Remove(tempGId);
					result = true;
				}

				this.ChartElementCalculatedTimeRange = TimeRange.Empty;
			}
			return result;
		}
		/// <summary>
		/// Smaže všechna data grafů z paměti, plus příslušné registry
		/// </summary>
		internal void ChartElementClear()
		{
			if (this.ChartElements == null)
				this.ChartElements = new Dictionary<GID, GridDataChartElementCls>();
			lock (this.ChartElements)
			{	// Zámek nám zajistí, že po dobu modifikace záznamů v this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
				this.ChartElements.Clear();
				this.ChartElementLoadedTimeRange = TimeRange.Empty;
				this.ChartElementCalculatedTimeRange = TimeRange.Empty;
				this.ChartElementCalculatedSize = Size.Empty;
				this.ChartVisibleList = new List<GridDataChartElementCls>();
				this._AllElementsLoaded = false;
			}
		}
		#endregion
		#region VÝPOČTY NAD ELEMENTY PO JEJICH NAČTENÍ, PODLE TYPU GRAFU
		/// <summary>
		/// Provede se po načtení dat časových elementů do dat tohoto řádku, pro graf typu TaskCapacityLink.
		/// Sloučí linky, které patří stejnému WorkGID a jsou přesně časově navazující (Element[x].End == Element[x+1].Begin).
		/// Vygeneruje čísla linek pro všechny načtené elementy, tak aby je bylo možno správně zobrazovat.
		/// Metoda vrací příznak změny výšky řádku.
		/// </summary>
		/// <returns>Příznak změny výšky řádku</returns>
		internal bool ChartElementsAfterReadLink()
		{
			if (this.RowItem.RowGraph != RowGraphMode.TaskCapacityLink) return false;      // Pojistka
			if (this.ChartElements == null || this.ChartElements.Count == 0) return false; // Nic nemáme

			// 1. Vytvořím si pole elementů typu GraphElementLayerType.ItemLayer, abych v něm mohl dále pracovat (třídit, procházet, modifikovat):
			List<GridDataChartElementCls> allElementList = new List<GridDataChartElementCls>(this.ChartElements.Values);
			List<GridDataChartElementCls> chartElementList = allElementList.FindAll(delegate(GridDataChartElementCls i) { return (i.DataElement.ElementLayer == GraphElementLayerType.ItemLayer); });

			// 2. Zajistíme, že časově kontinuální úseky náležející ke stejnému WorkGId budou vykreslovány jako fyzicky kontinuální:
			chartElementList.Sort(GridDataChartElementCls.CompareByWorkGIdBegin);          // Setřídit podle WorkGID a TimeRange.Begin
			GridDataChartElementCls firstElement = null;
			foreach (GridDataChartElementCls chartElement in chartElementList)
			{
				if (chartElement.DataElement.TimeRange.IsEmpty) continue;                  // Tento element je už sloučený do některého jiného elementu (viz konec této smyčky!)
				if (chartElement.DataElement.WorkGId.IsEmpty) continue;                    // Pro jistotu
				if (firstElement == null || chartElement.DataElement.WorkGId != firstElement.DataElement.WorkGId)
				{	// Narazili jsme na element, který patří k jinému Worku - zapamatujeme si nový WorkGID a hledáme další element:
					firstElement = chartElement;
					continue;
				}
				// Tento element patří ke stejnému WorkGIDu, jako předešlý:
				if (chartElement.DataElement.TimeRange.Begin != firstElement.DataElement.TimeRange.End)
				{	// ...ale nejsou kontinuální (tj. tento element nezačíná v okamžik, kdy předešlý element končí):
					firstElement = chartElement;
					continue;
				}
			}
			firstElement = null;

			// 3. Podle data počátku projdu elementy, a naskládám je do vizuálních linek tak, aby byly nekonfliktní:
			chartElementList.Sort(GridDataChartElementCls.CompareByTimeRangeBegin);        // Setřídit podle času počátku
			List<DateTime> endTimeList = new List<DateTime>();    // Seznam eviduje okamžik konce v jednotlivých linkách grafu pro tento řádek. Linka poskytuje časově kontinuální prostor vzájemně nekonfliktním položkám. Časově konfliktní jsou kresleny do jiných linek.
			foreach (GridDataChartElementCls chartElement in chartElementList)
			{
				int pipe = endTimeList.FindIndex(delegate(DateTime date) { return (date <= chartElement.DataElement.TimeRange.Begin); });
				if (pipe < 0)
				{	// Pro tento časový úsek nemáme žádnou volnou Pipe pro zobrazení => založíme novou a obsadíme ji naším časem:
					endTimeList.Add(chartElement.DataElement.TimeRange.End);
					pipe = endTimeList.Count - 1;
				}
				else
				{	// Našli jsme nějakou Pipe, jejíž obsazení končí před (nebo v) čase počátku našeho úkolu => obsadíme tuto Pipe až do konce našeho času:
					endTimeList[pipe] = chartElement.DataElement.TimeRange.End;
				}
				chartElement.PipeLine = pipe + 1;
			}

			// 4. Určíme výšku řádku podle počtu pipes a jednotkové výšky, a pozice Top jednotlivých Pipes:
			if (this.ChartPipeHeight == 0) this.ChartPipeHeight = 9;
			int pipeHeight = this.ChartPipeHeight;
			int pipeCount = endTimeList.Count + 1;                 // Počet Pipes = počet použitých + 1 volná (s indexem 0, je kreslena nahoře).
			this.ChartPipesY = new int[pipeCount];                 // Pole pro uložení výšky všech řádků
			int rowHeight = 4 + pipeCount * pipeHeight;            // Výška řádku vypočtená, aby řádek zobrazil všechny Pipes
			if (rowHeight < this.RowHeightDeclared) rowHeight = this.RowHeightDeclared; // Akceptuji minimální zadanou výšku řádku
			int topPipe = rowHeight - 2 - pipeHeight;              // Pozice Top té Pipe, která je úplně spodní = index [1]:
			for (int pipe = 1; pipe < pipeCount; pipe++)
			{
				this.ChartPipesY[pipe] = topPipe;
				topPipe -= pipeHeight;
			}
			this.ChartPipesY[0] = topPipe;

			bool rowHeightChanged = (rowHeight != this.RowHeight);
			this.RowHeight = rowHeight;

			//this.ChartElementsAfterReadCalculated = true;          // To není můj úkol, to řeší nadřízená procedura společně se zámkem !!!
			this.ChartElementCalculatedSize = Size.Empty;            // Nastavíme příznak, že vizuální pozice (pixely) nejsou řádně určeny. Při Draw dojde k přepočtu pozic elementů.
			return rowHeightChanged;
		}
		#endregion
		#region ŘÁDKOVÝ FILTR, TŘÍDĚNÍ
		/// <summary>
		/// Do svého řádku nastaví jeho viditelnost závislou na řádkovém filtru.
		/// </summary>
		/// <param name="rowSortFilter"></param>
		internal bool AcceptRowFilter(GridRowSortFilter rowSortFilter)
		{
			if (rowSortFilter != null)
				return rowSortFilter.IsVisibleByFilter(this.RowItem.Refer, 0) && rowSortFilter.IsVisibleByFilter(this.RowItem.Name, 1);
			else
				return true;
		}
		#endregion
	}
	#endregion
	#region CLASS GridDataVisualLineCls : pracovní dočasné podklady pro fyzické zobrazování dat (soupis jen viditelných řádků, vypočtené ikony, pozice řádků, výška, Rectangle)
	/// <summary>
	/// GridDataVisualLineCls : struktura, podklady pro fyzické zobrazování dat (soupis jen viditelných řádků, vypočtené ikony, pozice řádků, výška, Rectangle).
	/// Jeden záznam popisuje jeden viditelný řádek. Po zneviditelnění řádku (nadřízený node se zavře) tento objekt z paměti zmizí.
	/// Po každém načtení dalších dat, po každém otevření / zavření nodu se znovu celý seznam Rows[] vypočítá.
	/// </summary>
	internal class GridDataVisualLineCls
	{
		#region PROMĚNNÉ
		private int _RowIdx;
		private RowID _RowId;
		private GridDataRowCls _DataRow;
		private int _Top;
		private TreeLineType _IconAdded = TreeLineType.None;
		#endregion
		#region PROPERTY
		/// <summary>
		/// Index řádku.
		/// Je jednoznačný jen v rámci jednoho vytvořeného seznamu Rows.
		/// Seznam Rows se vytváří po každém načtení dat, po každém otevření/zavření nodu a po každé změně filtrování.
		/// Seznam obsahuje řádky v nepřerušované řadě { 0 - max }, kde RowIdx odpovídá indexu v seznamu Rows.
		/// Jakkoliv nalezený řádek tedy v sobě nese informaci o své pozici, a umožní tak najít i své sousední řádky.
		/// </summary>
		internal int RowIdx { get { return this._RowIdx; } }
		/// <summary>
		/// RowID řádku = kombinace GID a NodeId.
		/// </summary>
		internal RowID RowId { get { return this._RowId; } }
		/// <summary>
		/// ID nodu. Jde o trvalý klíč nodu v mapě stromu. ID nodu je permanentní a unikátní.
		/// ID nodu je klíčem ke všem akcím na vizuální úrovni, pro vizuální práci nelze využívat GID 
		/// (to je z vizuálního hlediska nejednoznačné: jeden GID se může zobrazit na více řádcích, pro různé parenty).
		/// </summary>
		internal int NodeId { get { return this._RowId.NodeId; } }
		/// <summary>
		/// GID dat v tomto řádku.
		/// Trvalá konstanta, odkazuje se na existující data v databázi.
		/// Nezmění se ani přenačtením dat, ani změnami seznamu.
		/// Ukazuje na logická data řádku, ale nelze používat k orientaci v mapě Tree, na to je třeba používat NodeId.
		/// </summary>
		internal GID DataGId { get { return this._DataRow.GId; } }
		/// <summary>
		/// Vlastní data z datového zdroje. Ta v sobě již nenesou informace o vizuální vrstvě, s výjimkou určení výšky řádku.
		/// </summary>
		internal GridDataRowCls DataRow { get { return this._DataRow; } }
		/// <summary>
		/// Pozice Top tohoto řádku v absolutní souřadnici. První řádek (s indexem 0) má Top = 0, 
		/// všechny další řádky mají svůj Top = (Minulý řádek.Top + .Height).
		/// Jde o jednoznačnou pozici řádku ve virtuálním dlouhém seznamu, z něhož se zobrazuje jen určitý výsek.
		/// </summary>
		internal int Top { get { return this._Top; } set { this._Top = value; } }
		/// <summary>
		/// Výška tohoto řádku. Fyzicky je uložena v this.DataRow.RowHeight.
		/// </summary>
		internal int Height { get { return (this._DataRow != null ? this._DataRow.RowHeight : 0); } set { if (this._DataRow != null) this._DataRow.RowHeight = value; } }
		/// <summary>
		/// Vypočítaná pozice Bottom tohoto řádku.
		/// Jde o pozici pixelu, na který de facto už se žádný objekt tohoto řádku nekreslí. Tak je to v grafice zvykem.
		/// </summary>
		internal int Bottom { get { return this._Top + this.Height; } }
		/// <summary>
		/// Ikony Tree tohoto řádku. Odpovídají pozici řádku v Tree, a odpovídají otevření/zavření nodu.
		/// </summary>
		internal TreeLineType[] IconTypes;
		/// <summary>
		/// Ikona stromu, která se výjimečně kreslí na místě začátku nodu. Obsahuje buď TreeLineType.None, anebo TreeLineType.ExpandedParent.
		/// Hodnota TreeLineType.ExpandedParent je počáteční vodící linkou k prvnímu ChildNodu, protože u řádků s velkou Height by to bez ní vypadalo blbě.
		/// </summary>
		internal TreeLineType IconAdded { get { return this._IconAdded; } }
		/// <summary>
		/// Plný konstruktor
		/// </summary>
		/// <param name="rowIdx">Index řádku</param>
		/// <param name="nodeId">ID nodu, který je zde zobrazen</param>
		/// <param name="dataRow">Data řádku</param>
		/// <param name="nodeTop">Top souřadnice</param>
		/// <param name="nodeHeight">Výška řádku</param>
		/// <param name="prevIcon">Předcházející ikony Tree</param>
		/// <param name="currIcon">Aktuální ikona Tree</param>
		/// <param name="addIcon">Přídavná ikona Tree (typicky TreeLineType.None, anebo TreeLineType.ExpandedParent)</param>
		internal GridDataVisualLineCls(int rowIdx, int nodeId, GridDataRowCls dataRow, int nodeTop, int nodeHeight, List<TreeLineType> prevIcon, TreeLineType currIcon, TreeLineType addIcon)
		{
			this._RowIdx = rowIdx;
			this._RowId = new RowID(dataRow.GId, nodeId);
			this._DataRow = dataRow;
			this._Top = nodeTop;
			this.Height = nodeHeight;
			this.IconTypes = new TreeLineType[prevIcon.Count + 1];
			prevIcon.CopyTo(this.IconTypes, 0);
			this.IconTypes[prevIcon.Count] = currIcon;
			this._IconAdded = addIcon;
		}
		/// <summary>
		/// Prázdný konstruktor
		/// </summary>
		/// <param name="currIcon"></param>
		internal GridDataVisualLineCls(TreeLineType currIcon)
		{
			this._RowIdx = -1;
			this._DataRow = null;
			this._Top = -1;
			this.Height = 0;
			this.IconTypes = new TreeLineType[0];
			this._IconAdded = TreeLineType.None;
		}
		#endregion
	}
	#endregion
	#region CLASS GridDataChartElementCls : potomek základní třídy Services.GraphElementDataCls data pro jednotlivou položku grafu v řádku
	/// <summary>
	/// GridDataChartElementCls : data pro jednotlivou položku grafu v řádku.
	/// Obsahuje jak data získaná z datového zdroje, tak data dopočítaná pro aktuální zobrazení.
	/// </summary>
	internal class GridDataChartElementCls : GraphElementDataCls
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="dataElement"></param>
		internal GridDataChartElementCls(IDataElement dataElement)
			: base(dataElement)
		{ }
		/// <summary>
		/// Nastaví hodnotu IsInEditingState
		/// </summary>
		/// <param name="editingState"></param>
		internal void SetEditingState(bool editingState)
		{
			IsInEditingState = editingState;
		}
		#endregion
		#region METODY PRO EDITACI ELEMENTU
		/// <summary>
		/// Metoda vymaže ty svoje vlastní data, která přestávají platit po transpozici do jiného řádku.
		/// </summary>
		internal void ClearOnTranspose()
		{
			this.DetailArea = Rectangle.Empty;
			this.ElementArea = Rectangle.Empty;
			this.PipeLine = 0;
			this.RectangleBack = Rectangle.Empty;
			this.VisualRectangle = Rectangle.Empty;
		}
		#endregion
		#region VÝPOČTY DAT GRAFU PRO TENTO ELEMENT
		/// <summary>
		/// Vypočte souřadnice jednotlivých částí grafu pro tento element, 
		/// na základě aktuálních fyzických souřadnic a na základě logických dat.
		/// Jde o vnitřek jednoho elementu (this).
		/// Typicky se určují dimenze jednotlivých složek políčka.
		/// V tuto chvíli je připravena hodnota do this.ElementArea, popisuje celý vnitřní prostor řádku (Y, height) a času (X, Width).
		/// </summary>
		/// <param name="rowGraph"></param>
		internal void PrepareVisualDataForGraph(RowGraphMode rowGraph, GridDataRowCls nodeData)
		{
			if (this.DataElement.ElementLayer == GraphElementLayerType.TempLayer)
				this._PrepareVisualDataForTemp(nodeData);
			else
			{
				switch (rowGraph)
				{
					case RowGraphMode.Gantt:
						this._PrepareVisualDataForGantt(nodeData);
						break;
					case RowGraphMode.CapacityUtilization:
						this._PrepareVisualDataForGraphCapacity(nodeData);
						break;
					case RowGraphMode.TaskCapacityLink:
						this._PrepareVisualDataForTaskLink(nodeData);
						break;
				}
			}
		}
		/// <summary>
		/// Připraví dimenze elementu pro element vrstvy Temporary.
		/// Takový element se vykreslí přes celou výšku řádku.
		/// </summary>
		private void _PrepareVisualDataForTemp(GridDataRowCls nodeData)
		{
			// Element je připraven, vykreslí se plná velikost elementu z this.ElementArea, nic víc není zapotřebí.
		}
		/// <summary>
		/// Připraví dimenze elementu pro graf typu Gantt.
		/// V tuto chvíli je připravena hodnota do this.ElementArea, popisuje celý vnitřní prostor řádku (Y, height) a času (X, Width).
		/// </summary>
		private void _PrepareVisualDataForGantt(GridDataRowCls nodeData)
		{
			// Gantt je připraven, vykreslí se plná velikost elementu z this.ElementArea, nic víc není zapotřebí.
		}
		/// <summary>
		/// Připraví dimenze elementu pro graf typu CapacityUtilization.
		/// V tuto chvíli je připravena hodnota do this.ElementArea, popisuje celý vnitřní prostor řádku (Y, height) a času (X, Width).
		/// </summary>
		private void _PrepareVisualDataForGraphCapacity(GridDataRowCls nodeData)
		{
			float standardUsage = 0.75F;

			int x = this.ElementArea.X;
			int w = this.ElementArea.Width;
			int b = this.ElementArea.Bottom;

			// a) pozadí kapacity :
			float standardHeight = standardUsage * (float)this.ElementArea.Height;         // Výška elementu, který zobrazuje 100%
			int height = (int)standardHeight;
			this.RectangleBack = new Rectangle(x, b - height, w, height);

			// b) ratio:
			float utilizationHeight = this.DataElement.UtilizationRatio * standardHeight;  // Výška elementu, který zobrazuje (UtilizationRatio)%
			height = (int)utilizationHeight;
			int y = b - height;
			this.ElementArea = new Rectangle(x, y, w, height);

			// c) top souřadnice vrcholku UtilizationRatio, relativně k this.ElementArea
			this.CapacityUsedTop = y;
		}
		/// <summary>
		/// Připraví dimenze elementu pro graf typu TaskCapacityLink.
		/// V tuto chvíli je připravena hodnota do this.ElementArea, popisuje celý vnitřní prostor řádku (Y, height) a času (X, Width).
		/// </summary>
		private void _PrepareVisualDataForTaskLink(GridDataRowCls nodeData)
		{
			//if (!nodeData.ChartElementsAfterReadCalculated)
			//	nodeData.ChartElementsAfterReadLink();

			// Tento graf zahrnuje dva typy objektů, odlišené hodnotou this.DataElement.ElementLayer :
			switch (this.DataElement.ElementLayer)
			{
				case GraphElementLayerType.SubLayer:
					// Stav kapacity - vykresluje se jako poloprůhledné pozadí grafu, v metodě GridGraphCls._PaintRowGraphCapacityBase, z dat v RectangleBack:
					this.RectangleBack = this.ElementArea;
					break;
				case GraphElementLayerType.ItemLayer:
					// Vlastní položka grafu - vykresluje se jako nízký obdélník.
					// Každá položka má určenu svoji PipeLine, každá PipeLine má v řádku uvedenu svoji souřadnici Y:
					int pipeLine = (this.PipeLine < 0 ? 0 : this.PipeLine);   // this.PipeLine == -1 jen výjimečně, běžně je 1 a více:
					if (this.CurrentlyTransposed) pipeLine = 0;               // Přesouvám nahoru / dolů: využívám volnou horní linku!
					this.ElementArea = new Rectangle(this.ElementArea.X, nodeData.ChartPipesY[pipeLine], this.ElementArea.Width, nodeData.ChartPipeHeight);

					// Následující řádek je citlivý na problémy s nedopočtením pipelines:
					// this.ElementArea = new Rectangle(this.ElementArea.X, nodeData.ChartPipesY[this.PipeLine], this.ElementArea.Width, nodeData.ChartPipeHeight);

					break;
			}
		}
		#endregion
	}
	#endregion

	// smazat konec.

}
